#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include <iostream>
// #include <assimp/cimport.h> // C importer
#include <assimp/Importer.hpp>
#include <assimp/scene.h> // collects data
#include <assimp/postprocess.h> // various extra operations
#include <stdlib.h> // memory management

bool load_mesh (const char* file_name, GLuint* vao, int* num_v_nprim)
{
	/* load file with assimp and print some stats */
	Assimp::Importer importer;
	const aiScene* scene = importer.ReadFile(file_name,
// 		aiProcess_CalcTangentSpace |
// 		aiProcess_SortByPType |
		aiProcess_JoinIdenticalVertices |
		aiProcess_Triangulate
	);

	if (!scene)
	{
// 		fprintf (stderr, "ERROR: reading mesh %s\n", file_name);
		std::cout << importer.GetErrorString() << std::endl;
		return false;
	}

	printf ("  %i animations\n", scene->mNumAnimations);
	printf ("  %i cameras\n", scene->mNumCameras);
	printf ("  %i lights\n", scene->mNumLights);
	printf ("  %i materials\n", scene->mNumMaterials);
	printf ("  %i meshes\n", scene->mNumMeshes);
	printf ("  %i textures\n", scene->mNumTextures);

	/* get first mesh in file only */
	const aiMesh* mesh = scene->mMeshes[0];
	printf ("    %i vertices in mesh[0]\n", mesh->mNumVertices);

	/* pass back number of vertex points in mesh */
// 	*point_count = mesh->mNumVertices;
// 	int* num_v_nprim = (int*) malloc(sizeof (int));
	*num_v_nprim = mesh->mNumFaces * 3; //Número de vértices não primitivos = Número de faces vezes o número de vértices/faces

	/* generate a VAO, using the pass-by-reference parameter that we give to the
	function */
	glGenVertexArrays(1, vao); //Cria um objeto para lembrar caracteristicas do buffer.
	glBindVertexArray(*vao); //Faz o atributo entrar em foco na maquina de estados.

	/* we really need to copy out all the data from AssImp's funny little data
	structures into pure contiguous arrays before we copy it into data buffers
	because assimp's texture coordinates are not really contiguous in memory.
	i allocate some dynamic memory to do this. */
	GLfloat* points = NULL; // array of vertex points
	GLfloat* normals = NULL; // array of vertex normals
	GLfloat* texcoords = NULL; // array of texture coordinates

	aiFace face;
	int k=0;

	if (mesh->HasPositions ()) 
	{
		points = (GLfloat*) malloc(*num_v_nprim * 3 * sizeof (GLfloat)); //Número de vértices vezes 3 (xyz, coordenadas)
		k=0;

		for(int i=0; i < mesh->mNumFaces; i++)
		{
			face = mesh->mFaces[i];

			for(int j=0; j<face.mNumIndices; j++)
			{
				const aiVector3D* vp = &(mesh->mVertices[ face.mIndices[j] ]); //mIndices = vértices/face
				points[k * 3] = (GLfloat)vp->x;
				points[k * 3 + 1] = (GLfloat)vp->y;
				points[k * 3 + 2] = (GLfloat)vp->z;
				k++;
			}
		}
	}

	if (mesh->HasNormals ())
	{
// 		normals = (GLfloat*)malloc (*num_v_nprim * 3 * sizeof (GLfloat));
// 		for (int i = 0; i < *num_v_nprim; i++)
// 		{
// 			const aiVector3D* vn = &(mesh->mNormals[i]);
// 			normals[i * 3] = (GLfloat)vn->x;
// 			normals[i * 3 + 1] = (GLfloat)vn->y;
// 			normals[i * 3 + 2] = (GLfloat)vn->z;
// 		}

		normals = (GLfloat*) malloc(*num_v_nprim * 3 * sizeof (GLfloat)); //Número de vértices vezes 3 (xyz, coordenadas)
		k=0;

		for(int i=0; i < mesh->mNumFaces; i++)
		{
			face = mesh->mFaces[i];

			for(int j=0; j<face.mNumIndices; j++)
			{
				const aiVector3D* vn = &(mesh->mNormals[ face.mIndices[j] ]); //mIndices = vértices/face
				normals[k * 3] = (GLfloat)vn->x;
				normals[k * 3 + 1] = (GLfloat)vn->y;
				normals[k * 3 + 2] = (GLfloat)vn->z;
				k++;
			}
		}
	}

	if (mesh->HasTextureCoords (0))
	{
// 		texcoords = (GLfloat*)malloc (*point_count * 2 * sizeof (GLfloat));
// 		for (int i = 0; i < *point_count; i++)
// 		{
// 			const aiVector3D* vt = &(mesh->mTextureCoords[0][i]);
// 			texcoords[i * 2] = (GLfloat)vt->x;
// 			texcoords[i * 2 + 1] = (GLfloat)vt->y;
// 		}

		texcoords = (GLfloat*) malloc(*num_v_nprim * 2 * sizeof (GLfloat)); //Número de vértices vezes 2 (xy, coordenadas)

		k=0;

		for(int i=0; i < mesh->mNumFaces; i++)
		{
			face = mesh->mFaces[i];

			for(int j=0; j<face.mNumIndices; j++)
			{
				const aiVector3D* vt = &(mesh->mTextureCoords[0][ face.mIndices[j] ]); //mIndices = vértices/face
				texcoords[k * 2] = (GLfloat)vt->x;
				texcoords[k * 2 + 1] = (GLfloat)vt->y;
				k++;
			}
		}
	}

	/* copy mesh data into VBOs */
	if (mesh->HasPositions ()) 
	{
		GLuint vbo;
		glGenBuffers(1, &vbo);
		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		glBufferData(GL_ARRAY_BUFFER, 3 * *num_v_nprim * sizeof (GLfloat), points, GL_STATIC_DRAW);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
		glEnableVertexAttribArray(0);
		free(points); // free our temporary memory
	}

	if (mesh->HasNormals ())
	{
		GLuint vbo;
		glGenBuffers(1, &vbo); //Cria um buffer zerado.
		glBindBuffer(GL_ARRAY_BUFFER, vbo); //Dá "Bind" no buffer, pra transformar ele no buffer principal.
		glBufferData(GL_ARRAY_BUFFER, 3 * *num_v_nprim * sizeof (GLfloat), normals, GL_STATIC_DRAW); // Copia os pontos pro buffer principal.
		glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL); //Define o layout do buffer bindado.
		glEnableVertexAttribArray(1); //Argumento referente ao número do layout do vertex_shader
		free(normals); // free our temporary memory
	}

	if (mesh->HasTextureCoords (0))
	{
		GLuint vbo;
		glGenBuffers(1, &vbo);
		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		glBufferData(GL_ARRAY_BUFFER, 2 * *num_v_nprim * sizeof (GLfloat), texcoords, GL_STATIC_DRAW);
		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, NULL);
		glEnableVertexAttribArray(2);
		free(texcoords); // free our temporary memory
	}

	if (mesh->HasTangentsAndBitangents ()) {
		// NB: could store/print tangents here
	}
  
  
  /* free assimp's copy of memory */
//   aiReleaseImport (scene);
  printf ("mesh loaded\n");

  return true;
}